using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using System.Security.Cryptography;
using System.Text;
using System.IO;
using Auto9Slicer;

public class SpriteLibraryPostprocessor : AssetPostprocessor
{
    void OnPreprocessTexture()
    {
        var importer = assetImporter as TextureImporter;
        var path = this.assetPath;
        if(path.StartsWith("Assets/SpriteLibrary/"))
        {
            string guid = AssetDatabase.AssetPathToGUID(path);
            var info = SpriteLibrary.GetAutoSlicedMd5(path);
            var md5 = info.md5;
            SpriteLibrary.WriteMd5Cache(guid, md5);
        }
    }


}

public static class SpriteLibrary
{
    [MenuItem("com.pzy.*/Sprite Library/Redirect")]
    public static void Process()
    {
        var active = Selection.activeObject;
        var redirectedCount = 0;
        var newCount = 0;
        if(active is GameObject)
        {
            var prefab = active as GameObject;
            var imageList = prefab.GetComponentsInChildren<Image>(true);
            foreach(var image in imageList)
            {
                var result = ProcessImage(image);
                if(result == ProcessResult.None)
                {

                }
                else if(result == ProcessResult.Redirected)
                {
                    EditorUtility.SetDirty(active);
                    redirectedCount++;
                }
                else if(result == ProcessResult.NewAddAndRedirected)
                {
                    EditorUtility.SetDirty(active);
                    redirectedCount++;
                    newCount++;
                }
            }
            AssetDatabase.SaveAssetIfDirty(prefab);
        }
        Debug.Log($"[SpriteLibrary] {redirectedCount} redirected ({newCount} new).");

    }

    static Dictionary<string, string> _md5ToGuidDic;
    
    static Dictionary<string, string> Md5ToGuidDic
    {
        get
        {
            if(_md5ToGuidDic == null)
            {
                _md5ToGuidDic = new Dictionary<string, string>();
                string[] guids = AssetDatabase.FindAssets("t:sprite", new[] { "Assets/SpriteLibrary" });
                foreach (string guid in guids)
                {
                    string md5 = GetMd5Cache(guid);
                    if(md5 == null)
                    {
                        var path = AssetDatabase.GUIDToAssetPath(guid);
                        var info = GetAutoSlicedMd5(path);
                        md5 = info.md5;
                        WriteMd5Cache(guid, md5);
                    }
                    _md5ToGuidDic[md5] = guid;
                }
            }
            return _md5ToGuidDic;
        }
    }

    public static void WriteMd5Cache(string guid, string md5)
    {
        var dir = "Library/SpriteLibrary";
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        var path = $"{dir}/{guid}";
        File.WriteAllText(path, md5);
    }

    static string GetMd5Cache(string guid)
    {
        var dir = "Library/SpriteLibrary";
        if (!Directory.Exists(dir))
        {
            return null;
        }
        var path = $"{dir}/{guid}";
        if(!File.Exists(path))
        {
            return null;
        }
        var md5 = File.ReadAllText(path);
        return md5;
    }

    enum ProcessResult
    {
        None,
        Redirected,
        NewAddAndRedirected,
    }

    static ProcessResult ProcessImage(Image image)
    {
        var sprite = image.sprite;
        var path = AssetDatabase.GetAssetPath(sprite);
        if(path.StartsWith("Assets/SpriteLibrary/"))
        {
            return ProcessResult.None;
        }
        if(!path.StartsWith("Assets/"))
        {
            return ProcessResult.None;
        }
        var info = GetAutoSlicedMd5(path, false);
        var md5 = info.md5;
        if(Md5ToGuidDic.ContainsKey(md5))
        {
            var librarySpriteGuid = Md5ToGuidDic[md5];
            var librarySpritePath = AssetDatabase.GUIDToAssetPath(librarySpriteGuid);
            var librarySprite = AssetDatabase.LoadAssetAtPath<Sprite>(librarySpritePath);
            image.sprite = librarySprite;
            AutoSetImageType(image);
            GameObject.DestroyImmediate(info.textureInfo.Texture);
            return ProcessResult.Redirected;
        }
        else
        {
            var toPath = $"Assets/SpriteLibrary/new/{sprite.name}.png";
            toPath = SelectSafePath(toPath);
            //WriteAllBytes(toPath, info.textureInfo.Texture.EncodeToPNG());
            WriteAllBytes(toPath, info.textureInfo.Texture.EncodeToPNG());
            AssetDatabase.ImportAsset(toPath);

            var importer = AssetImporter.GetAtPath(toPath) as TextureImporter;
            importer.textureType = TextureImporterType.Sprite;
            importer.spriteBorder = info.textureInfo.Border.ToVector4();
            importer.SaveAndReimport();

            var guid = AssetDatabase.AssetPathToGUID(toPath);
            Md5ToGuidDic[md5] = guid;

            var librarySprite = AssetDatabase.LoadAssetAtPath<Sprite>(toPath);
            image.sprite = librarySprite;
            AutoSetImageType(image);
            GameObject.DestroyImmediate(info.textureInfo.Texture);
            return ProcessResult.NewAddAndRedirected;
        }
    }

    public static void WriteAllBytes(string path, byte[] data)
    {
        var dir = Path.GetDirectoryName(path);
        if(!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        File.WriteAllBytes(path, data);
    }

    static string SelectSafePath(string path)
    {
        if(File.Exists(path))
        {
            var dir = Path.GetDirectoryName(path);
            var ext = Path.GetExtension(path);
            var filename = Path.GetFileName(path);
            var index = 1;
            here:
            var newPath = $"{dir}/{filename} ({index}){ext}";
            if(File.Exists(newPath))
            {
                index++;
                goto here;
            }
            return newPath;
        }
        return path;
    }

    static void AutoSetImageType(Image image)
    {
        var sprite = image.sprite;
        if(sprite == null)
        {
            return;
        }
        if (image.type == Image.Type.Simple || image.type == Image.Type.Sliced)
        {
            if (sprite.border != Vector4.zero)
            {
                image.type = Image.Type.Sliced;
            }
            else
            {
                image.type = Image.Type.Simple;
            }
        }
    }

    public class SlicedInfo
    {
        public SlicedTexture textureInfo;
        public string md5;
        public byte[] pngData;
    }

    public static SlicedInfo GetAutoSlicedMd5(string filePath, bool autoDestoryMemoryTexture = true)
    {
        var targetTexture = new Texture2D(2, 2);
        var bytes = File.ReadAllBytes(filePath);
        targetTexture.LoadImage(bytes);
        var slicedTexture = Slicer.Slice(targetTexture, SliceOptions.Default);
        GameObject.DestroyImmediate(targetTexture);
        var md5 = GetMd5(slicedTexture.Texture);
        var info = new SlicedInfo();
        info.textureInfo = slicedTexture;
        info.md5 = md5;
        if(autoDestoryMemoryTexture)
        {
            GameObject.DestroyImmediate(info.textureInfo.Texture);
        }
        return info;
    }

    static string GetMd5(Texture2D texture)
    {
        var md5 = new MD5CryptoServiceProvider();
        var w = texture.width;
        var h = texture.height;
        var data = new byte[w * h * 4];
        for (int i = 0; i < w; i++)
        {
            for (int j = 0; j < h; j++)
            {
                var color = (Color32)texture.GetPixel(i, j);
                var index = (j * w + i)*4;
                var a = color.a;
                var r = color.r;
                var g = color.g;
                var b = color.b;
                data[index] = a;
                data[index + 1] = r;
                data[index + 2] = g;
                data[index + 3] = b;
            }
        }
        var hash = md5.ComputeHash(data);
        var sb = new StringBuilder();
        foreach (byte b in hash)
        {
            sb.Append(b.ToString("x2"));
        }
        return sb.ToString();
    }
}
